This is an R Markdown
Notebook. Each section of the code is then explained.
First of all import the libraries needed
#install.packages(c("datavolley", "ovlytics"))
library(datavolley)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
library(ggplot2)
Avvertimento: il pacchetto ‘ggplot2’ è stato creato con R versione 4.3.2
library(dplyr)
Avvertimento: il pacchetto ‘dplyr’ è stato creato con R versione 4.3.2
Caricamento pacchetto: ‘dplyr’
I seguenti oggetti sono mascherati da ‘package:stats’:
filter, lag
I seguenti oggetti sono mascherati da ‘package:base’:
intersect, setdiff, setequal, union
library(ovlytics)
Import the file you are interested in considering more than one
match, you have to import all the folder
filename <- "C:/Users/mirko/Documents/GitHub/CuneoWebsite.io/Assets/&##Backup##_R00 HONDA O-ALLIANZ V.dvw"
#d <- dir("C:/Users/mirko/OneDrive - Politecnico di Milano/Altro/Volley/Conco2324/Parella Torino/Ritorno/", pattern = "dvw$", full.names = TRUE)
teamName = 'HONDA OLIVERO S.BERNARDO CUNEO'
x <- dv_read(filename)
Avvertimento: stri_enc_detect2 is deprecated and will be removed in a future release of 'stringi'.
serve_idx <- find_serves(plays(x))
table(plays(x)$team[serve_idx])
ALLIANZ VERO VOLLEY MILANO HONDA OLIVERO S.BERNARDO CUNEO
74 64
Funzioni utili
## find rows where a single player is on court
player_on_court <- function(x, target_player_id, team = NULL) {
if (!is.null(team)) team <- match.arg(team, c("home", "visiting"))
## 'team' is optional here, if NULL then we look at both home and visiting teams
idx <- rep(FALSE, nrow(x))
if (is.null(team) || team == "home") {
idx <- idx | x$home_player_id1 == target_player_id | x$home_player_id2 == target_player_id | x$home_player_id3 == target_player_id |
x$home_player_id4 == target_player_id | x$home_player_id5 == target_player_id | x$home_player_id6 == target_player_id
}
if (is.null(team) || team == "visiting") {
idx <- idx | x$visiting_player_id1 == target_player_id | x$visiting_player_id2 == target_player_id | x$visiting_player_id3 == target_player_id |
x$visiting_player_id4 == target_player_id | x$visiting_player_id5 == target_player_id | x$visiting_player_id6 == target_player_id
}
idx[is.na(idx)] <- FALSE
idx
}
## find rows where any of our target players are on court
any_player_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ANY of those players were on court
apply(do.call(cbind, out), 1, any)
}
## find rows where all of our target players are on court
all_players_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ALL of those players were on court
apply(do.call(cbind, out), 1, all)
}
d <- dir("C:/Users/mirko/Documents/GitHub/CuneoWebsite.io/Assets/", pattern = "dvw$", full.names = TRUE)
lx <- list()
## read each file
for (fi in seq_along(d)) lx[[fi]] <- dv_read(d[fi], insert_technical_timeouts = FALSE)
Avvertimento: stri_enc_detect2 is deprecated and will be removed in a future release of 'stringi'.
## now extract the play-by-play component from each and bind them together
px <- list()
for (fi in seq_along(lx)) px[[fi]] <- plays(lx[[fi]])
px <- do.call(rbind, px)
Rendimento in Battuta
#, end_zone == 5
library("formattable")
Avvertimento: il pacchetto ‘formattable’ è stato creato con R versione 4.3.3Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
table_data <- px %>%
dplyr::filter(skill == "Serve", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_battute = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_battute, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_battute, digits = 0),
)
data_plot <- table_data
table_data
# Calculate cumulative statistics for the team
team_total <- table_data %>%
summarise(
N_battute = sum(N_battute),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_battute), digits = 0),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_battute), digits = 0)
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Combine the team total row with the original table data
table_data_with_total <- bind_rows(table_data, team_total)
# Print the table with the team total row
table_data_with_total
library(kableExtra)
Avvertimento: il pacchetto ‘kableExtra’ è stato creato con R versione 4.3.2
Caricamento pacchetto: ‘kableExtra’
Il seguente oggetto è mascherato da ‘package:dplyr’:
group_rows
# Apply custom CSS styling to the entire table
# Reorder columns to make positività the second column
table_data <- table_data_with_total %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(9, bold = TRUE) %>%
column_spec(2, background = ifelse(table_data$efficienza >= percent(0.3), "lightgreen",ifelse(table_data$efficienza > percent(0.25) & table_data$efficienza < percent(0.3), "yellow", "lightcoral")))
#row_spec(which(table_data$efficienza > 0.2 & table_data$efficienza < 0.3), background = "yellow") %>%
#row_spec(which(table_data$efficienza <= 0.2), background = "lightcoral")
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Battuta_tab.html")
library(plotly)
Avvertimento: il pacchetto ‘plotly’ è stato creato con R versione 4.3.2
Caricamento pacchetto: ‘plotly’
Il seguente oggetto è mascherato da ‘package:formattable’:
style
Il seguente oggetto è mascherato da ‘package:ggplot2’:
last_plot
Il seguente oggetto è mascherato da ‘package:stats’:
filter
Il seguente oggetto è mascherato da ‘package:graphics’:
layout
fig <- plot_ly(data_plot,
x = ~positività*100,
y = ~efficienza*100,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività (%)</b>: %{x})',
'<br><b>Efficienza (%)</b>: %{y}',
'<br><b>Battuta</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_battute, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Battuta',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
# Assuming you have your data frame 'data_plot' with columns 'player_name' and 'efficienza'
# Calculate the color based on efficienza values
data_plot$color <- ifelse(data_plot$efficienza < 0.25, "red",
ifelse(data_plot$efficienza > 0.30, "green", "yellow"))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Battuta",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Battuta",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
# Customize other plot settings as needed
library(htmlwidgets)
Avvertimento: il pacchetto ‘htmlwidgets’ è stato creato con R versione 4.3.2
# Assuming 'fig' is your Plotly figure
saveWidget(fig, "Battuta.html")
Rendimento in Ricezione
Ora analizziamo la ricezione:
#, end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Reception", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_receptions = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_receptions, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_receptions, digits = 0),
)
data_plot <- table_data
table_data
# Compute total statistics for the team
total_stats <- table_data %>%
summarise(
N_receptions = sum(N_receptions),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_receptions)),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_receptions))
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Combine the team total row with the original table data
table_data_with_total <- bind_rows(table_data, total_stats)
# Print the table with the team total row
table_data_with_total
table_data <- table_data_with_total %>%
#select(-efficienza) %>%
select(everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(nrow(table_data), bold = TRUE) %>%
column_spec(2,
background = case_when(
(table_data$player_name == "Serena Scognamillo" | table_data$player_name == "Federica Ferrario") ~ ifelse(table_data$efficienza >= percent(0.56), "lightgreen", ifelse(table_data$efficienza > percent(0.48) & table_data$efficienza < percent(0.56), "yellow", "lightcoral")),
(table_data$player_name == "Lena Stigrot" | table_data$player_name == "Anna Haak") ~ ifelse(table_data$efficienza >= percent(0.37), "lightgreen", ifelse(table_data$efficienza > percent(0.31) & table_data$efficienza < percent(0.37), "yellow", "lightcoral")),
(table_data$player_name == "Alice Tanase" | table_data$player_name == "Madison Kubik") ~ ifelse(table_data$efficienza >= percent(0.43), "lightgreen", ifelse(table_data$efficienza > percent(0.37) & table_data$efficienza < percent(0.43), "yellow", "lightcoral")),
TRUE ~ ifelse(table_data$efficienza >= percent(0.42), "lightgreen", ifelse(table_data$efficienza > percent(0.41) & table_data$efficienza < percent(0.42), "orange", "red"))
)
)
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Ricezione_tab.html")
fig <- plot_ly(data_plot,
x = ~positività*100,
y = ~efficienza*100,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività(%)</b>: %{x}',
'<br><b>Efficienza(%)</b>: %{y}',
'<br><b>Ricezione</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_receptions, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Ricezione',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
# Assuming you have your data frame 'data_plot' with columns 'player_name' and 'efficienza'
# Calculate the color based on efficienza values
data_plot <- table_data %>%
mutate(color = case_when(
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza >= percent(0.56)) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza >= percent(0.37)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza >= percent(0.43)) |
efficienza >= percent(0.42) ~ "lightgreen",
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza > percent(0.48) & efficienza < percent(0.56)) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza > percent(0.31) & efficienza < percent(0.37)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza > percent(0.37) & efficienza < percent(0.43)) |
(efficienza > percent(0.41) & efficienza < percent(0.42)) ~ "orange",
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza <= percent(0.48) ) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza <= percent(0.31)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza <= percent(0.37)) |
efficienza < percent(0.41) ~ "lightcoral",
TRUE ~ NA_character_
))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Ricezione",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Ricezione",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
saveWidget(fig, "Ricezione.html")
Rendimento in Attacco
# end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Attack", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_attacks = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_attacks, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_attacks, digits = 0),
)
data_plot <- table_data
table_data
# Compute the total statistics
total_stats <- table_data %>%
summarise(
N_attacks = sum(N_attacks),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_attacks)),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_attacks))
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Add the total row to the table data
table_data <- bind_rows(table_data, total_stats)
table_data <- table_data %>%
#select(-efficienza) %>%
select(everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(nrow(table_data), bold = TRUE) %>%
column_spec(2, background = case_when(
(table_data$player_name == "Anna Adelusi" | table_data$player_name == "Terry Ruth Enweonwu") ~ ifelse(table_data$efficienza >= percent(0.27), "lightgreen", ifelse(table_data$efficienza > percent(0.24) & table_data$efficienza < percent(0.27), "yellow", "lightcoral")),
(table_data$player_name == "Anna Haak" | table_data$player_name == "Lena Stigrot") ~ ifelse(table_data$efficienza >= percent(0.34), "lightgreen", ifelse(table_data$efficienza > percent(0.30) & table_data$efficienza < percent(0.34), "yellow", "lightcoral")),
(table_data$player_name == "Alice Tanase" | table_data$player_name == "Madison Kubik") ~ ifelse(table_data$efficienza >= percent(0.24), "lightgreen", ifelse(table_data$efficienza > percent(0.20) & table_data$efficienza < percent(0.24), "yellow", "lightcoral")),
(table_data$player_name == "Saly Thior" | table_data$player_name == "Amandha Sylves" | table_data$player_name == "Anna Hall" | table_data$player_name == "Beatrice Molinaro") ~ ifelse(table_data$efficienza >= percent(0.44), "lightgreen", ifelse(table_data$efficienza > percent(0.38) & table_data$efficienza < percent(0.44), "yellow", "lightcoral")),
TRUE ~ ifelse(table_data$efficienza >= percent(0.42), "lightgreen", ifelse(table_data$efficienza > percent(0.41) & table_data$efficienza < percent(0.42), "orange", "red"))
)
)
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Attacco_tab.html")
fig <- plot_ly(data_plot,
x = ~positività,
y = ~efficienza,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività(%)</b>: %{x}',
'<br><b>Efficienza(%)</b>: %{y}',
'<br><b>Attacchi</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_attacks, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Attacco',
xaxis = list(title = 'Positività', showgrid = FALSE),
yaxis = list(title = 'Efficienza', showgrid = FALSE)
)
fig
# Add a new column 'color' based on the rules provided
data_plot <- table_data %>%
mutate(color = case_when(
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza >= percent(0.27)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza >= percent(0.34)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza >= percent(0.24)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza >= percent(0.44)) |
efficienza >= percent(0.42) ~ "lightgreen",
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza > percent(0.24) & efficienza < percent(0.27)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza > percent(0.30) & efficienza < percent(0.34)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza > percent(0.20) & efficienza < percent(0.24)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza > percent(0.38) & efficienza < percent(0.44)) |
(efficienza > percent(0.41) & efficienza < percent(0.42)) ~ "orange",
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza <= percent(0.24)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza <= percent(0.30)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza <= percent(0.20)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza <= percent(0.38)) |
efficienza < percent(0.41) ~ "lightcoral",
TRUE ~ NA_character_
))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Attacco",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Attacco",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
saveWidget(fig, "Attacco.html")
Volleyball Distribution
library(ggplot2)
library(htmlwidgets)
for (i in 1:6) {
attack_rate <- px %>%
dplyr::filter(skill == "Attack", team == teamName, visiting_setter_position == i) %>%
group_by(start_zone) %>%
dplyr::summarize(n_attacks = n()) %>%
mutate(rate = n_attacks/sum(n_attacks)) %>%
ungroup
attack_rate <- cbind(attack_rate, dv_xy(attack_rate$start_zone, end = "lower"))
tm2i <- attack_rate$team == teams(px)[2]
attack_rate[tm2i, c("x", "y")] <- dv_flip_xy(attack_rate[tm2i, c("x", "y")])
attack_rate$rate_rounded <- round(attack_rate$rate, 2)
fig2 <- ggplot(attack_rate, aes(x, y, fill = rate_rounded)) +
geom_tile() +
geom_text(aes(label = paste0(round(rate_rounded * 100, 2), "%")), color = "black", size = 3) +
ggcourt(labels = teams(px)) +
scale_fill_gradient2(name = "Attack rate (%)", labels = scales::percent_format(accuracy = 0.01))
html_name <- paste0("Distribuzione", i, ".png")
# Save the plot as an HTML file
ggsave(html_name, plot = fig2, width = 8, height = 6, units = "in", dpi = 300)
}
LS0tDQp0aXRsZTogIkN1bmVvIERhdGEgQW5hbHlzaXMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIEVhY2ggc2VjdGlvbiBvZiB0aGUgY29kZSBpcyB0aGVuIGV4cGxhaW5lZC4NCg0KRmlyc3Qgb2YgYWxsIGltcG9ydCB0aGUgbGlicmFyaWVzIG5lZWRlZA0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKGMoImRhdGF2b2xsZXkiLCAib3ZseXRpY3MiKSkNCmxpYnJhcnkoZGF0YXZvbGxleSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG92bHl0aWNzKQ0KYGBgDQoNCkltcG9ydCB0aGUgZmlsZSB5b3UgYXJlIGludGVyZXN0ZWQgaW4gY29uc2lkZXJpbmcgbW9yZSB0aGFuIG9uZSBtYXRjaCwgeW91IGhhdmUgdG8gaW1wb3J0IGFsbCB0aGUgZm9sZGVyDQoNCmBgYHtyfQ0KZmlsZW5hbWUgPC0gIkM6L1VzZXJzL21pcmtvL0RvY3VtZW50cy9HaXRIdWIvQ3VuZW9XZWJzaXRlLmlvL0Fzc2V0cy8mIyNCYWNrdXAjI19SMDAgSE9OREEgTy1BTExJQU5aIFYuZHZ3Ig0KI2QgPC0gZGlyKCJDOi9Vc2Vycy9taXJrby9PbmVEcml2ZSAtIFBvbGl0ZWNuaWNvIGRpIE1pbGFuby9BbHRyby9Wb2xsZXkvQ29uY28yMzI0L1BhcmVsbGEgVG9yaW5vL1JpdG9ybm8vIiwgcGF0dGVybiA9ICJkdnckIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQp0ZWFtTmFtZSA9ICdIT05EQSBPTElWRVJPIFMuQkVSTkFSRE8gQ1VORU8nDQp4IDwtIGR2X3JlYWQoZmlsZW5hbWUpDQpzZXJ2ZV9pZHggPC0gZmluZF9zZXJ2ZXMocGxheXMoeCkpDQp0YWJsZShwbGF5cyh4KSR0ZWFtW3NlcnZlX2lkeF0pDQpgYGANCg0KRnVuemlvbmkgdXRpbGkNCg0KYGBge3J9DQojIyBmaW5kIHJvd3Mgd2hlcmUgYSBzaW5nbGUgcGxheWVyIGlzIG9uIGNvdXJ0DQpwbGF5ZXJfb25fY291cnQgPC0gZnVuY3Rpb24oeCwgdGFyZ2V0X3BsYXllcl9pZCwgdGVhbSA9IE5VTEwpIHsNCiAgaWYgKCFpcy5udWxsKHRlYW0pKSB0ZWFtIDwtIG1hdGNoLmFyZyh0ZWFtLCBjKCJob21lIiwgInZpc2l0aW5nIikpDQogICMjICd0ZWFtJyBpcyBvcHRpb25hbCBoZXJlLCBpZiBOVUxMIHRoZW4gd2UgbG9vayBhdCBib3RoIGhvbWUgYW5kIHZpc2l0aW5nIHRlYW1zDQogIGlkeCA8LSByZXAoRkFMU0UsIG5yb3coeCkpDQogIGlmIChpcy5udWxsKHRlYW0pIHx8IHRlYW0gPT0gImhvbWUiKSB7DQogICAgaWR4IDwtIGlkeCB8IHgkaG9tZV9wbGF5ZXJfaWQxID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JGhvbWVfcGxheWVyX2lkMiA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCRob21lX3BsYXllcl9pZDMgPT0gdGFyZ2V0X3BsYXllcl9pZCB8DQogICAgICAgICAgICAgICAgIHgkaG9tZV9wbGF5ZXJfaWQ0ID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JGhvbWVfcGxheWVyX2lkNSA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCRob21lX3BsYXllcl9pZDYgPT0gdGFyZ2V0X3BsYXllcl9pZA0KICB9DQogIGlmIChpcy5udWxsKHRlYW0pIHx8IHRlYW0gPT0gInZpc2l0aW5nIikgew0KICAgIGlkeCA8LSBpZHggfCB4JHZpc2l0aW5nX3BsYXllcl9pZDEgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkMiA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCR2aXNpdGluZ19wbGF5ZXJfaWQzID09IHRhcmdldF9wbGF5ZXJfaWQgfA0KICAgICAgICAgICAgICAgICB4JHZpc2l0aW5nX3BsYXllcl9pZDQgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkNSA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCR2aXNpdGluZ19wbGF5ZXJfaWQ2ID09IHRhcmdldF9wbGF5ZXJfaWQNCiAgfQ0KICBpZHhbaXMubmEoaWR4KV0gPC0gRkFMU0UNCiAgaWR4DQp9DQoNCiMjIGZpbmQgcm93cyB3aGVyZSBhbnkgb2Ygb3VyIHRhcmdldCBwbGF5ZXJzIGFyZSBvbiBjb3VydA0KYW55X3BsYXllcl9vbl9jb3VydCA8LSBmdW5jdGlvbih4LCB0YXJnZXRfcGxheWVyX2lkcywgdGVhbSA9IE5VTEwpIHsNCiAgIyMgZm9yIGVhY2ggdGFyZ2V0IHBsYXllciwgZmluZCByb3dzIHdoZXJlIHRoZXkgYXJlIG9uIGNvdXJ0DQogIG91dCA8LSBsYXBwbHkodGFyZ2V0X3BsYXllcl9pZHMsIGZ1bmN0aW9uKHBpZCkgcGxheWVyX29uX2NvdXJ0KHgsIHRhcmdldF9wbGF5ZXJfaWQgPSBwaWQsIHRlYW0gPSB0ZWFtKSkNCiAgIyMgYW5kIG5vdyBmaW5kIHJvd3Mgd2hlcmUgQU5ZIG9mIHRob3NlIHBsYXllcnMgd2VyZSBvbiBjb3VydA0KICBhcHBseShkby5jYWxsKGNiaW5kLCBvdXQpLCAxLCBhbnkpDQp9DQoNCiMjIGZpbmQgcm93cyB3aGVyZSBhbGwgb2Ygb3VyIHRhcmdldCBwbGF5ZXJzIGFyZSBvbiBjb3VydA0KYWxsX3BsYXllcnNfb25fY291cnQgPC0gZnVuY3Rpb24oeCwgdGFyZ2V0X3BsYXllcl9pZHMsIHRlYW0gPSBOVUxMKSB7DQogICMjIGZvciBlYWNoIHRhcmdldCBwbGF5ZXIsIGZpbmQgcm93cyB3aGVyZSB0aGV5IGFyZSBvbiBjb3VydA0KICBvdXQgPC0gbGFwcGx5KHRhcmdldF9wbGF5ZXJfaWRzLCBmdW5jdGlvbihwaWQpIHBsYXllcl9vbl9jb3VydCh4LCB0YXJnZXRfcGxheWVyX2lkID0gcGlkLCB0ZWFtID0gdGVhbSkpDQogICMjIGFuZCBub3cgZmluZCByb3dzIHdoZXJlIEFMTCBvZiB0aG9zZSBwbGF5ZXJzIHdlcmUgb24gY291cnQNCiAgYXBwbHkoZG8uY2FsbChjYmluZCwgb3V0KSwgMSwgYWxsKQ0KfQ0KDQpgYGANCg0KYGBge3J9DQpkIDwtIGRpcigiQzovVXNlcnMvbWlya28vRG9jdW1lbnRzL0dpdEh1Yi9DdW5lb1dlYnNpdGUuaW8vQXNzZXRzLyIsIHBhdHRlcm4gPSAiZHZ3JCIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KbHggPC0gbGlzdCgpDQojIyByZWFkIGVhY2ggZmlsZQ0KZm9yIChmaSBpbiBzZXFfYWxvbmcoZCkpIGx4W1tmaV1dIDwtIGR2X3JlYWQoZFtmaV0sIGluc2VydF90ZWNobmljYWxfdGltZW91dHMgPSBGQUxTRSkNCiMjIG5vdyBleHRyYWN0IHRoZSBwbGF5LWJ5LXBsYXkgY29tcG9uZW50IGZyb20gZWFjaCBhbmQgYmluZCB0aGVtIHRvZ2V0aGVyDQpweCA8LSBsaXN0KCkNCmZvciAoZmkgaW4gc2VxX2Fsb25nKGx4KSkgcHhbW2ZpXV0gPC0gcGxheXMobHhbW2ZpXV0pDQpweCA8LSBkby5jYWxsKHJiaW5kLCBweCkNCg0KYGBgDQoNCiMjIFJlbmRpbWVudG8gaW4gQmF0dHV0YQ0KDQpgYGB7cn0NCiMsIGVuZF96b25lID09IDUNCmxpYnJhcnkoImZvcm1hdHRhYmxlIikgDQp0YWJsZV9kYXRhIDwtIHB4ICU+JSANCiAgZHBseXI6OmZpbHRlcihza2lsbCA9PSAiU2VydmUiLCB0ZWFtID09IHRlYW1OYW1lKSAlPiUgDQogIGdyb3VwX2J5KHBsYXllcl9uYW1lKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpemUoDQogICAgTl9iYXR0dXRlID0gbigpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiIyIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIrIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfZXNjYWxhbWF0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiISIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X25lZ2F0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiLSIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiPSIsIG5hLnJtID0gVFJVRSksDQogICAgcG9zaXRpdml0w6AgPSBwZXJjZW50KChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKS9OX2JhdHR1dGUsIGRpZ2l0cyA9IDApLA0KICAgIGVmZmljaWVuemEgPSBwZXJjZW50KChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKS9OX2JhdHR1dGUsIGRpZ2l0cyA9IDApLA0KICApDQoNCmRhdGFfcGxvdCA8LSB0YWJsZV9kYXRhDQoNCnRhYmxlX2RhdGENCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIGN1bXVsYXRpdmUgc3RhdGlzdGljcyBmb3IgdGhlIHRlYW0NCnRlYW1fdG90YWwgPC0gdGFibGVfZGF0YSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE5fYmF0dHV0ZSA9IHN1bShOX2JhdHR1dGUpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGNvdW50X3BlcmZldHRlKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShjb3VudF9wb3NpdGl2ZSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGNvdW50X2Vycm9yaSksDQogICAgcG9zaXRpdml0w6AgPSBwZXJjZW50KHN1bShjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKSAvIHN1bShOX2JhdHR1dGUpLCBkaWdpdHMgPSAwKSwNCiAgICBlZmZpY2llbnphID0gcGVyY2VudChzdW0oY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkgLyBzdW0oTl9iYXR0dXRlKSwgZGlnaXRzID0gMCkNCiAgKSAlPiUNCiAgbXV0YXRlKHBsYXllcl9uYW1lID0gIlRPVC4gU3F1YWRyYSIpICAjIEFkZCBhIHBsYXllcl9uYW1lIGZvciB0aGUgdGVhbSB0b3RhbCByb3cNCg0KIyBDb21iaW5lIHRoZSB0ZWFtIHRvdGFsIHJvdyB3aXRoIHRoZSBvcmlnaW5hbCB0YWJsZSBkYXRhDQp0YWJsZV9kYXRhX3dpdGhfdG90YWwgPC0gYmluZF9yb3dzKHRhYmxlX2RhdGEsIHRlYW1fdG90YWwpDQoNCiMgUHJpbnQgdGhlIHRhYmxlIHdpdGggdGhlIHRlYW0gdG90YWwgcm93DQp0YWJsZV9kYXRhX3dpdGhfdG90YWwNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0KIyBSZW9yZGVyIGNvbHVtbnMgdG8gbWFrZSBwb3NpdGl2aXTDoCB0aGUgc2Vjb25kIGNvbHVtbg0KdGFibGVfZGF0YSA8LSB0YWJsZV9kYXRhX3dpdGhfdG90YWwgJT4lDQogICNzZWxlY3QoLWVmZmljaWVuemEpICU+JQ0KICBzZWxlY3QoMSwgNywgZXZlcnl0aGluZygpKQ0KDQojIEFwcGx5IGN1c3RvbSBDU1Mgc3R5bGluZyB0byB0aGUgZW50aXJlIHRhYmxlDQpzdHlsZWRfdGFibGUgPC0gdGFibGVfZGF0YSAlPiUNCiAga2FibGUoImh0bWwiKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGh0bWx0YWJsZV9jbGFzcyA9ICdzdHlsZWQtdGFibGUnLCBodG1sX2ZvbnQgPSAnIkJlIFZpZXRuYW0gUHJvIiwgc2Fucy1zZXJpZicpICU+JQ0KICByb3dfc3BlYyg5LCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDIsIGJhY2tncm91bmQgPSBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC4zKSwgImxpZ2h0Z3JlZW4iLGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuMjUpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjMpLCAieWVsbG93IiwgImxpZ2h0Y29yYWwiKSkpDQoNCiAgI3Jvd19zcGVjKHdoaWNoKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IDAuMiAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IDAuMyksIGJhY2tncm91bmQgPSAieWVsbG93IikgJT4lDQogICNyb3dfc3BlYyh3aGljaCh0YWJsZV9kYXRhJGVmZmljaWVuemEgPD0gMC4yKSwgYmFja2dyb3VuZCA9ICJsaWdodGNvcmFsIikNCg0KIyBTYXZlIHRoZSBzdHlsZWQgdGFibGUgdG8gYW4gSFRNTCBmaWxlDQp3cml0ZUxpbmVzKGFzLmNoYXJhY3RlcihzdHlsZWRfdGFibGUpLCAiQmF0dHV0YV90YWIuaHRtbCIpDQoNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkocGxvdGx5KQ0KDQpmaWcgPC0gcGxvdF9seShkYXRhX3Bsb3QsIA0KICAgICAgICAgICAgICAgeCA9IH5wb3NpdGl2aXTDoCoxMDAsIA0KICAgICAgICAgICAgICAgeSA9IH5lZmZpY2llbnphKjEwMCwNCiAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIA0KICAgICAgICAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgDQogICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gcGFzdGUoJzxpPlBsYXllcjwvaT46ICV7dGV4dH0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+UG9zaXRpdml0w6AgKCUpPC9iPjogJXt4fSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+RWZmaWNpZW56YSAoJSk8L2I+OiAle3l9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPkJhdHR1dGE8L2I+OiAle21hcmtlci5zaXplfTxleHRyYT48L2V4dHJhPicpLA0KICAgICAgICAgICAgICAgY29sb3IgPSB+cG9zaXRpdml0w6AsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+Tl9iYXR0dXRlLCBzaXplbW9kZSA9ICJhcmVhIiwgc2l6ZXJlZiA9IDAuMDA1LCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIEJhdHR1dGEnLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQb3NpdGl2aXTDoCcsIHNob3dncmlkID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0VmZmljaWVuemEnLCBzaG93Z3JpZCA9IFRSVUUpDQopDQoNCmZpZw0KYGBgDQoNCmBgYHtyfQ0KIyBBc3N1bWluZyB5b3UgaGF2ZSB5b3VyIGRhdGEgZnJhbWUgJ2RhdGFfcGxvdCcgd2l0aCBjb2x1bW5zICdwbGF5ZXJfbmFtZScgYW5kICdlZmZpY2llbnphJw0KDQojIENhbGN1bGF0ZSB0aGUgY29sb3IgYmFzZWQgb24gZWZmaWNpZW56YSB2YWx1ZXMNCmRhdGFfcGxvdCRjb2xvciA8LSBpZmVsc2UoZGF0YV9wbG90JGVmZmljaWVuemEgPCAwLjI1LCAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGRhdGFfcGxvdCRlZmZpY2llbnphID4gMC4zMCwgImdyZWVuIiwgInllbGxvdyIpKQ0KDQoNCmZpZyA8LSBwbG90X2x5KA0KICB4ID0gZGF0YV9wbG90JHBsYXllcl9uYW1lLA0KICB5ID0gcm91bmQoZGF0YV9wbG90JGVmZmljaWVuemEsIGRpZ2l0cyA9IDIpLA0KICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gZGF0YV9wbG90JGNvbG9yKSwNCiAgbmFtZSA9ICJFZmZpY2llbnphIGluIEJhdHR1dGEiLA0KICB0eXBlID0gImJhciIsDQogIHRleHQgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksIHRleHRwb3NpdGlvbiA9ICdhdXRvJywgbWFya2VyID0gbGlzdChjb2xvciA9ICdyZ2IoMTU4LDIwMiwyMjUpJywgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiKDgsNDgsMTA3KScsIHdpZHRoID0gMS41KSkpDQoNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJFZmZpY2llbnphIGluIEJhdHR1dGEiLA0KDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksDQoNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkNCg0KDQpmaWcNCg0KIyBDdXN0b21pemUgb3RoZXIgcGxvdCBzZXR0aW5ncyBhcyBuZWVkZWQNCg0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCg0KIyBBc3N1bWluZyAnZmlnJyBpcyB5b3VyIFBsb3RseSBmaWd1cmUNCnNhdmVXaWRnZXQoZmlnLCAiQmF0dHV0YS5odG1sIikNCmBgYA0KDQojIyBSZW5kaW1lbnRvIGluIFJpY2V6aW9uZQ0KDQpPcmEgYW5hbGl6emlhbW8gbGEgcmljZXppb25lOg0KDQpgYGB7cn0NCiMsIGVuZF96b25lID09IDUNCnRhYmxlX2RhdGEgPC0gcHggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNraWxsID09ICJSZWNlcHRpb24iLCB0ZWFtID09IHRlYW1OYW1lKSAlPiUgDQogIGdyb3VwX2J5KHBsYXllcl9uYW1lKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpemUoDQogICAgTl9yZWNlcHRpb25zID0gbigpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiIyIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIrIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfZXNjYWxhbWF0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiISIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X25lZ2F0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiLSIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiPSIsIG5hLnJtID0gVFJVRSksDQogICAgcG9zaXRpdml0w6AgPSBwZXJjZW50KChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKS9OX3JlY2VwdGlvbnMsIGRpZ2l0cyA9IDApLA0KICAgIGVmZmljaWVuemEgPSBwZXJjZW50KChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKS9OX3JlY2VwdGlvbnMsIGRpZ2l0cyA9IDApLA0KICApDQoNCmRhdGFfcGxvdCA8LSB0YWJsZV9kYXRhDQoNCnRhYmxlX2RhdGENCmBgYA0KDQpgYGB7cn0NCiMgQ29tcHV0ZSB0b3RhbCBzdGF0aXN0aWNzIGZvciB0aGUgdGVhbQ0KdG90YWxfc3RhdHMgPC0gdGFibGVfZGF0YSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE5fcmVjZXB0aW9ucyA9IHN1bShOX3JlY2VwdGlvbnMpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGNvdW50X3BlcmZldHRlKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShjb3VudF9wb3NpdGl2ZSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGNvdW50X2Vycm9yaSksDQogICAgcG9zaXRpdml0w6AgPSBwZXJjZW50KHN1bShjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKSAvIHN1bShOX3JlY2VwdGlvbnMpKSwNCiAgICBlZmZpY2llbnphID0gcGVyY2VudChzdW0oY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkgLyBzdW0oTl9yZWNlcHRpb25zKSkNCiAgKSAlPiUNCiAgbXV0YXRlKHBsYXllcl9uYW1lID0gIlRPVC4gU3F1YWRyYSIpICAjIEFkZCBhIHBsYXllcl9uYW1lIGZvciB0aGUgdGVhbSB0b3RhbCByb3cNCg0KIyBDb21iaW5lIHRoZSB0ZWFtIHRvdGFsIHJvdyB3aXRoIHRoZSBvcmlnaW5hbCB0YWJsZSBkYXRhDQp0YWJsZV9kYXRhX3dpdGhfdG90YWwgPC0gYmluZF9yb3dzKHRhYmxlX2RhdGEsIHRvdGFsX3N0YXRzKQ0KDQojIFByaW50IHRoZSB0YWJsZSB3aXRoIHRoZSB0ZWFtIHRvdGFsIHJvdw0KdGFibGVfZGF0YV93aXRoX3RvdGFsDQpgYGANCg0KYGBge3J9DQp0YWJsZV9kYXRhIDwtIHRhYmxlX2RhdGFfd2l0aF90b3RhbCAlPiUNCiAgI3NlbGVjdCgtZWZmaWNpZW56YSkgJT4lDQogIHNlbGVjdChldmVyeXRoaW5nKCkpDQoNCiMgQXBwbHkgY3VzdG9tIENTUyBzdHlsaW5nIHRvIHRoZSBlbnRpcmUgdGFibGUNCnN0eWxlZF90YWJsZSA8LSB0YWJsZV9kYXRhICU+JQ0KICBrYWJsZSgiaHRtbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbHRhYmxlX2NsYXNzID0gJ3N0eWxlZC10YWJsZScsIGh0bWxfZm9udCA9ICciQmUgVmlldG5hbSBQcm8iLCBzYW5zLXNlcmlmJykgJT4lDQogIHJvd19zcGVjKG5yb3codGFibGVfZGF0YSksIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgDQogICAgICAgICAgICAgIGJhY2tncm91bmQgPSBjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIlNlcmVuYSBTY29nbmFtaWxsbyIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJGZWRlcmljYSBGZXJyYXJpbyIpIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNTYpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDgpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjU2KSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpLA0KICAgICAgICAgICAgICAgICh0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJMZW5hIFN0aWdyb3QiIHwgdGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQW5uYSBIYWFrIikgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC4zNyksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4zMSkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuMzcpLCAieWVsbG93IiwgImxpZ2h0Y29yYWwiKSksDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFsaWNlIFRhbmFzZSIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJNYWRpc29uIEt1YmlrIikgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC40MyksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4zNykgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDMpLCAieWVsbG93IiwgImxpZ2h0Y29yYWwiKSksDQogICAgICAgICAgICAgICAgVFJVRSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQyKSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjQxKSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MiksICJvcmFuZ2UiLCAicmVkIikpDQogICAgICAgICAgICAgICkNCiAgICAgICAgICAgICAgKQ0KDQojIFNhdmUgdGhlIHN0eWxlZCB0YWJsZSB0byBhbiBIVE1MIGZpbGUNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKHN0eWxlZF90YWJsZSksICJSaWNlemlvbmVfdGFiLmh0bWwiKQ0KYGBgDQoNCmBgYHtyfQ0KZmlnIDwtIHBsb3RfbHkoZGF0YV9wbG90LCANCiAgICAgICAgICAgICAgIHggPSB+cG9zaXRpdml0w6AqMTAwLCANCiAgICAgICAgICAgICAgIHkgPSB+ZWZmaWNpZW56YSoxMDAsDQogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCANCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsIA0KICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKCc8aT5QbGF5ZXI8L2k+OiAle3RleHR9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPlBvc2l0aXZpdMOgKCUpPC9iPjogJXt4fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5FZmZpY2llbnphKCUpPC9iPjogJXt5fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5SaWNlemlvbmU8L2I+OiAle21hcmtlci5zaXplfTxleHRyYT48L2V4dHJhPicpLA0KICAgICAgICAgICAgICAgY29sb3IgPSB+cG9zaXRpdml0w6AsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+Tl9yZWNlcHRpb25zLCBzaXplbW9kZSA9ICJhcmVhIiwgc2l6ZXJlZiA9IDAuMDA1LCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIFJpY2V6aW9uZScsDQogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1Bvc2l0aXZpdMOgJywgc2hvd2dyaWQgPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnRWZmaWNpZW56YScsIHNob3dncmlkID0gVFJVRSkNCikNCmZpZw0KYGBgDQoNCmBgYHtyfQ0KIyBBc3N1bWluZyB5b3UgaGF2ZSB5b3VyIGRhdGEgZnJhbWUgJ2RhdGFfcGxvdCcgd2l0aCBjb2x1bW5zICdwbGF5ZXJfbmFtZScgYW5kICdlZmZpY2llbnphJw0KDQojIENhbGN1bGF0ZSB0aGUgY29sb3IgYmFzZWQgb24gZWZmaWNpZW56YSB2YWx1ZXMNCmRhdGFfcGxvdCA8LSB0YWJsZV9kYXRhICU+JQ0KICBtdXRhdGUoY29sb3IgPSBjYXNlX3doZW4oDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiU2VyZW5hIFNjb2duYW1pbGxvIiwgIkZlZGVyaWNhIEZlcnJhcmlvIikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC41NikpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiTGVuYSBTdGlncm90IiwgIkFubmEgSGFhayIpICYgZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMzcpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkFsaWNlIFRhbmFzZSIsICJNYWRpc29uIEt1YmlrIikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC40MykpIHwNCiAgICBlZmZpY2llbnphID49IHBlcmNlbnQoMC40MikgfiAibGlnaHRncmVlbiIsDQogICAgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiU2VyZW5hIFNjb2duYW1pbGxvIiwgIkZlZGVyaWNhIEZlcnJhcmlvIikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjQ4KSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNTYpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkxlbmEgU3RpZ3JvdCIsICJBbm5hIEhhYWsiKSAmIGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzEpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4zNykpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzcpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MykpIHwNCiAgICAoZWZmaWNpZW56YSA+IHBlcmNlbnQoMC40MSkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjQyKSkgfiAib3JhbmdlIiwNCiAgICANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJTZXJlbmEgU2NvZ25hbWlsbG8iLCAiRmVkZXJpY2EgRmVycmFyaW8iKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjQ4KSApIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiTGVuYSBTdGlncm90IiwgIkFubmEgSGFhayIpICYgZWZmaWNpZW56YSA8PSBwZXJjZW50KDAuMzEpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkFsaWNlIFRhbmFzZSIsICJNYWRpc29uIEt1YmlrIikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC4zNykpIHwNCiAgICBlZmZpY2llbnphIDwgcGVyY2VudCgwLjQxKSB+ICJsaWdodGNvcmFsIiwNCiAgICANCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICApKQ0KDQoNCg0KZmlnIDwtIHBsb3RfbHkoDQogIHggPSBkYXRhX3Bsb3QkcGxheWVyX25hbWUsDQogIHkgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksDQogIG1hcmtlciA9IGxpc3QoY29sb3IgPSBkYXRhX3Bsb3QkY29sb3IpLA0KICBuYW1lID0gIkVmZmljaWVuemEgaW4gUmljZXppb25lIiwNCiAgdHlwZSA9ICJiYXIiLA0KICB0ZXh0ID0gcm91bmQoZGF0YV9wbG90JGVmZmljaWVuemEsIGRpZ2l0cyA9IDIpLCB0ZXh0cG9zaXRpb24gPSAnYXV0bycsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAncmdiKDE1OCwyMDIsMjI1KScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYig4LDQ4LDEwNyknLCB3aWR0aCA9IDEuNSkpKQ0KDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAiRWZmaWNpZW56YSBpbiBSaWNlemlvbmUiLA0KDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksDQoNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkNCg0KDQpmaWcNCmBgYA0KDQpgYGB7cn0NCnNhdmVXaWRnZXQoZmlnLCAiUmljZXppb25lLmh0bWwiKQ0KYGBgDQoNCiMjIFJlbmRpbWVudG8gaW4gQXR0YWNjbw0KDQpgYGB7cn0NCiMgIGVuZF96b25lID09IDUNCnRhYmxlX2RhdGEgPC0gcHggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNraWxsID09ICJBdHRhY2siLCB0ZWFtID09IHRlYW1OYW1lKSAlPiUgDQogIGdyb3VwX2J5KHBsYXllcl9uYW1lKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpemUoDQogICAgTl9hdHRhY2tzID0gbigpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiIyIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIrIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfZXNjYWxhbWF0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiISIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X25lZ2F0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiLSIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiPSIsIG5hLnJtID0gVFJVRSksDQogICAgcG9zaXRpdml0w6AgPSBwZXJjZW50KChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKS9OX2F0dGFja3MsIGRpZ2l0cyA9IDApLA0KICAgIGVmZmljaWVuemEgPSBwZXJjZW50KChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKS9OX2F0dGFja3MsIGRpZ2l0cyA9IDApLA0KICApDQoNCmRhdGFfcGxvdCA8LSB0YWJsZV9kYXRhDQoNCnRhYmxlX2RhdGENCmBgYA0KDQpgYGB7cn0NCiMgQ29tcHV0ZSB0aGUgdG90YWwgc3RhdGlzdGljcw0KdG90YWxfc3RhdHMgPC0gdGFibGVfZGF0YSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE5fYXR0YWNrcyA9IHN1bShOX2F0dGFja3MpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGNvdW50X3BlcmZldHRlKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShjb3VudF9wb3NpdGl2ZSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGNvdW50X2Vycm9yaSksDQogICAgcG9zaXRpdml0w6AgPSBwZXJjZW50KHN1bShjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKSAvIHN1bShOX2F0dGFja3MpKSwNCiAgICBlZmZpY2llbnphID0gcGVyY2VudChzdW0oY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkgLyBzdW0oTl9hdHRhY2tzKSkNCiAgKSAlPiUNCiAgbXV0YXRlKHBsYXllcl9uYW1lID0gIlRPVC4gU3F1YWRyYSIpICAjIEFkZCBhIHBsYXllcl9uYW1lIGZvciB0aGUgdGVhbSB0b3RhbCByb3cNCg0KIyBBZGQgdGhlIHRvdGFsIHJvdyB0byB0aGUgdGFibGUgZGF0YQ0KdGFibGVfZGF0YSA8LSBiaW5kX3Jvd3ModGFibGVfZGF0YSwgdG90YWxfc3RhdHMpDQpgYGANCg0KYGBge3J9DQp0YWJsZV9kYXRhIDwtIHRhYmxlX2RhdGEgJT4lDQogICNzZWxlY3QoLWVmZmljaWVuemEpICU+JQ0KICBzZWxlY3QoZXZlcnl0aGluZygpKQ0KDQojIEFwcGx5IGN1c3RvbSBDU1Mgc3R5bGluZyB0byB0aGUgZW50aXJlIHRhYmxlDQpzdHlsZWRfdGFibGUgPC0gdGFibGVfZGF0YSAlPiUNCiAga2FibGUoImh0bWwiKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGh0bWx0YWJsZV9jbGFzcyA9ICdzdHlsZWQtdGFibGUnLCBodG1sX2ZvbnQgPSAnIkJlIFZpZXRuYW0gUHJvIiwgc2Fucy1zZXJpZicpICU+JQ0KICByb3dfc3BlYyhucm93KHRhYmxlX2RhdGEpLCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDIsIGJhY2tncm91bmQgPSBjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFubmEgQWRlbHVzaSIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJUZXJyeSBSdXRoIEVud2Vvbnd1IikgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC4yNyksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4yNCkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuMjcpLCAieWVsbG93IiwgImxpZ2h0Y29yYWwiKSksDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFubmEgSGFhayIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJMZW5hIFN0aWdyb3QiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjM0KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjMwKSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4zNCksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQWxpY2UgVGFuYXNlIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIk1hZGlzb24gS3ViaWsiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI0KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjIwKSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4yNCksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiU2FseSBUaGlvciIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbWFuZGhhIFN5bHZlcyIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbm5hIEhhbGwiIHwgdGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQmVhdHJpY2UgTW9saW5hcm8iKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQ0KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjM4KSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40NCksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICBUUlVFIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDIpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDEpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjQyKSwgIm9yYW5nZSIsICJyZWQiKSkNCiAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgICApDQoNCiMgU2F2ZSB0aGUgc3R5bGVkIHRhYmxlIHRvIGFuIEhUTUwgZmlsZQ0Kd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoc3R5bGVkX3RhYmxlKSwgIkF0dGFjY29fdGFiLmh0bWwiKQ0KYGBgDQoNCmBgYHtyfQ0KZmlnIDwtIHBsb3RfbHkoZGF0YV9wbG90LCANCiAgICAgICAgICAgICAgIHggPSB+cG9zaXRpdml0w6AsIA0KICAgICAgICAgICAgICAgeSA9IH5lZmZpY2llbnphLA0KICAgICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgDQogICAgICAgICAgICAgICBtb2RlID0gJ21hcmtlcnMnLCANCiAgICAgICAgICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZSgnPGk+UGxheWVyPC9pPjogJXt0ZXh0fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5Qb3NpdGl2aXTDoCglKTwvYj46ICV7eH0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+RWZmaWNpZW56YSglKTwvYj46ICV7eX0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+QXR0YWNjaGk8L2I+OiAle21hcmtlci5zaXplfTxleHRyYT48L2V4dHJhPicpLA0KICAgICAgICAgICAgICAgY29sb3IgPSB+cG9zaXRpdml0w6AsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+Tl9hdHRhY2tzLCBzaXplbW9kZSA9ICJhcmVhIiwgc2l6ZXJlZiA9IDAuMDA1LCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIEF0dGFjY28nLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQb3NpdGl2aXTDoCcsIHNob3dncmlkID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdFZmZpY2llbnphJywgc2hvd2dyaWQgPSBGQUxTRSkNCikNCg0KZmlnDQpgYGANCg0KYGBge3J9DQojIEFkZCBhIG5ldyBjb2x1bW4gJ2NvbG9yJyBiYXNlZCBvbiB0aGUgcnVsZXMgcHJvdmlkZWQNCmRhdGFfcGxvdCA8LSB0YWJsZV9kYXRhICU+JQ0KICBtdXRhdGUoY29sb3IgPSBjYXNlX3doZW4oDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBBZGVsdXNpIiwgIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI3KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEhhYWsiLCAiTGVuYSBTdGlncm90IikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC4zNCkpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI0KSkgfA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNhbHkgVGhpb3IiLCAiQW1hbmRoYSBTeWx2ZXMiLCAiQW5uYSBIYWxsIiwgIkJlYXRyaWNlIE1vbGluYXJvIikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC40NCkpIHwNCiAgICBlZmZpY2llbnphID49IHBlcmNlbnQoMC40MikgfiAibGlnaHRncmVlbiIsDQogICAgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBBZGVsdXNpIiwgIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSAmIGVmZmljaWVuemEgPiBwZXJjZW50KDAuMjQpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4yNykpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBIYWFrIiwgIkxlbmEgU3RpZ3JvdCIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4zMCkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjM0KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4yMCkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjI0KSkgfA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNhbHkgVGhpb3IiLCAiQW1hbmRoYSBTeWx2ZXMiLCAiQW5uYSBIYWxsIiwgIkJlYXRyaWNlIE1vbGluYXJvIikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjM4KSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDQpKSB8DQogICAgKGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDEpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MikpIH4gIm9yYW5nZSIsDQogICAgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBBZGVsdXNpIiwgIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjI0KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEhhYWsiLCAiTGVuYSBTdGlncm90IikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC4zMCkpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjIwKSkgfA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNhbHkgVGhpb3IiLCAiQW1hbmRoYSBTeWx2ZXMiLCAiQW5uYSBIYWxsIiwgIkJlYXRyaWNlIE1vbGluYXJvIikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC4zOCkpIHwNCiAgICBlZmZpY2llbnphIDwgcGVyY2VudCgwLjQxKSB+ICJsaWdodGNvcmFsIiwNCiAgICANCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICApKQ0KDQoNCmZpZyA8LSBwbG90X2x5KA0KICB4ID0gZGF0YV9wbG90JHBsYXllcl9uYW1lLA0KICB5ID0gcm91bmQoZGF0YV9wbG90JGVmZmljaWVuemEsIGRpZ2l0cyA9IDIpLA0KICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gZGF0YV9wbG90JGNvbG9yKSwNCiAgbmFtZSA9ICJFZmZpY2llbnphIGluIEF0dGFjY28iLA0KICB0eXBlID0gImJhciIsDQogIHRleHQgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksIHRleHRwb3NpdGlvbiA9ICdhdXRvJywgbWFya2VyID0gbGlzdChjb2xvciA9ICdyZ2IoMTU4LDIwMiwyMjUpJywgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiKDgsNDgsMTA3KScsIHdpZHRoID0gMS41KSkpDQoNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJFZmZpY2llbnphIGluIEF0dGFjY28iLA0KDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksDQoNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkNCg0KDQpmaWcNCmBgYA0KDQpgYGB7cn0NCnNhdmVXaWRnZXQoZmlnLCAiQXR0YWNjby5odG1sIikNCmBgYA0KDQojIyBWb2xsZXliYWxsIERpc3RyaWJ1dGlvbg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQoNCmZvciAoaSBpbiAxOjYpIHsNCiAgYXR0YWNrX3JhdGUgPC0gcHggJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoc2tpbGwgPT0gIkF0dGFjayIsIHRlYW0gPT0gdGVhbU5hbWUsIHZpc2l0aW5nX3NldHRlcl9wb3NpdGlvbiA9PSBpKSAlPiUNCiAgICBncm91cF9ieShzdGFydF96b25lKSAlPiUgDQogICAgZHBseXI6OnN1bW1hcml6ZShuX2F0dGFja3MgPSBuKCkpICU+JQ0KICAgIG11dGF0ZShyYXRlID0gbl9hdHRhY2tzL3N1bShuX2F0dGFja3MpKSAlPiUgDQogICAgdW5ncm91cA0KDQogIGF0dGFja19yYXRlIDwtIGNiaW5kKGF0dGFja19yYXRlLCBkdl94eShhdHRhY2tfcmF0ZSRzdGFydF96b25lLCBlbmQgPSAibG93ZXIiKSkNCg0KICB0bTJpIDwtIGF0dGFja19yYXRlJHRlYW0gPT0gdGVhbXMocHgpWzJdDQogIGF0dGFja19yYXRlW3RtMmksIGMoIngiLCAieSIpXSA8LSBkdl9mbGlwX3h5KGF0dGFja19yYXRlW3RtMmksIGMoIngiLCAieSIpXSkNCg0KICBhdHRhY2tfcmF0ZSRyYXRlX3JvdW5kZWQgPC0gcm91bmQoYXR0YWNrX3JhdGUkcmF0ZSwgMikNCg0KICBmaWcyIDwtIGdncGxvdChhdHRhY2tfcmF0ZSwgYWVzKHgsIHksIGZpbGwgPSByYXRlX3JvdW5kZWQpKSArIA0KICAgIGdlb21fdGlsZSgpICsgDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChyb3VuZChyYXRlX3JvdW5kZWQgKiAxMDAsIDIpLCAiJSIpKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMykgKw0KICAgIGdnY291cnQobGFiZWxzID0gdGVhbXMocHgpKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIobmFtZSA9ICJBdHRhY2sgcmF0ZSAoJSkiLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpDQoNCiAgaHRtbF9uYW1lIDwtIHBhc3RlMCgiRGlzdHJpYnV6aW9uZSIsIGksICIucG5nIikNCiAgDQogICMgU2F2ZSB0aGUgcGxvdCBhcyBhbiBIVE1MIGZpbGUNCiAgZ2dzYXZlKGh0bWxfbmFtZSwgcGxvdCA9IGZpZzIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLCBkcGkgPSAzMDApDQp9DQoNCmBgYA0K